#ifndef _M_FORMAT_H__
#define _M_FORMAT_H__

#include "util.hpp"
#include "logLevel.hpp"
#include "logMessage.hpp"
#include <iostream>
#include <vector>
#include <memory>
#include <unordered_set>
#include <sstream>
#include <cassert>

namespace Yohifo
{
    // 格式化写入的父类
    class FormatItem
    {
    public:
        using ptr = std::shared_ptr<FormatItem>;
        virtual void format(std::ostream &out, const LogMessage &msg) = 0;
    };

    // 时间、日志器、线程ID、源文件名称、源文件行号、日志等级、日志主体消息
    class TimeFormatItem : public FormatItem
    {
    public:
        TimeFormatItem(const std::string &fmt)
            : _fmt(fmt)
        {
        }

        void format(std::ostream &out, const LogMessage &msg) override
        {
            // 根据时间戳获取时间相关结构体
            struct tm *pt = localtime(&msg._ctime);

            // 根据指定的格式 fmt 获取时间字符串
            char buffer[32] = {0};
            size_t n = strftime(buffer, sizeof(buffer) - 1, _fmt.c_str(), pt);
            out << buffer;
        }

    private:
        std::string _fmt; // 时间的格式
    };

    class LoggerFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, const LogMessage &msg) override
        {
            out << msg._logger;
        }
    };

    class ThreadIDFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, const LogMessage &msg) override
        {
            out << msg._tid;
        }
    };

    class FileFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, const LogMessage &msg) override
        {
            out << msg._file;
        }
    };

    class LineFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, const LogMessage &msg) override
        {
            out << msg._line;
        }
    };

    class LevelFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, const LogMessage &msg) override
        {
            out << LogLevel::toString(msg._level);
        }
    };

    class MsgFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, const LogMessage &msg) override
        {
            out << msg._payload;
        }
    };

    class TabFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, const LogMessage &msg) override
        {
            out << "\t";
        }
    };

    class NewLineFormatItem : public FormatItem
    {
    public:
        void format(std::ostream &out, const LogMessage &msg) override
        {
            out << "\n";
        }
    };

    class OtherFormatItem : public FormatItem
    {
    public:
        OtherFormatItem() {}
        OtherFormatItem(const std::string &str) : _str(str) {}

        void format(std::ostream &out, const LogMessage &msg) override
        {
            out << _str;
        }

    private:
        std::string _str;
    };

    // 日志格式化类
    class Formater : public FormatItem
    {
    public:
        using ptr = std::shared_ptr<Formater>; // 管理具体的日志格式化对象
    public:
        Formater(const std::string &pattern = "[%d{%Y/%m/%d %H:%M:%S}][%c][%t][%f:%l][%p]%T%m%n")
            : _pattern(pattern)
        {
            // 需要检查解析函数是否正常工作
            assert(parsePattern());
        }

        // 对信息进行格式化处理
        void format(std::ostream &out, const LogMessage &msg) override // 重写纯虚函数
        {
            // 遍历 _items 调用对应对象的 format 格式化子项函数
            for (auto &it : _items)
                it->format(out, msg);
        }

        std::string format(const LogMessage &msg)
        {
            // 将信息格式化后，形成字符串
            std::stringstream ss; // 利用切片机制，父类对象接收子类
            format(ss, msg);      // 可以通过多态的特性，实现运算符的重写及正确调用
            return ss.str();
        }

    private:
        // 根据字符串格式化规则进行解析
        bool parsePattern()
        {
            // 没有以 % 起始统计的，都算其他字符串
            //    需要额外注意一下 %% 的情况
            // 其他情况，都是格式化的 key
            //    日期的格式化需要特殊处理，提取出后面的日期格式信息串
            // 注意各种越界+不合法的匹配情况

            // 注册合法的格式化字符信息
            std::vector<std::string> vs{"d", "c", "t", "f", "l", "p", "T", "m", "n"};
            std::unordered_set<std::string> hash(vs.begin(), vs.end());

            std::vector<std::pair<std::string, std::string>> fmt_order;
            std::string key, val;

            // _patten = [%d{%Y/%m/%d %H:%M:%S}][%c][%t][%f:%l][%p]%T%m%n
            size_t pos = 0;
            size_t n = _pattern.size();
            while (pos < _pattern.size())
            {
                // 判断该处理其他字符串还是格式化字符串
                if (_pattern[pos] != '%')
                {
                    // 一直往后走，直到遇到 %
                    while (pos < n && _pattern[pos] != '%')
                        val += _pattern[pos++];

                    // 甄别是否出现 %% 的特殊情况
                    if (pos + 1 < n && _pattern[pos + 1] == '%')
                    {
                        val += _pattern[pos];
                        pos += 2; // 向后走两步，跳过 "%%"
                    }
                }
                else
                {
                    // 首先跳过 %
                    pos++;

                    // 甄别是否出现 %% 的特殊情况
                    if (pos < n && _pattern[pos] == '%')
                    {
                        val += _pattern[pos];
                        pos++; // 跳过 "%%"

                        // 这种情况太特殊了比如 %%%d、[%%%%d]
                        fmt_order.push_back(std::make_pair(key, val));
                        val.clear();
                        continue;
                    }

                    // 判断合法性
                    if (pos >= n)
                    {
                        std::cerr << "% 之后无指定格式" << std::endl;
                        return false;
                    }

                    // 获取格式化字符
                    key += _pattern[pos++];

                    if (!hash.count(key))
                    {
                        std::cerr << "指定的格式 %" << key << " 尚未注册" << std::endl;
                        return false;
                    }

                    // 判断是否为 日期
                    if (key == "d")
                    {
                        // 首先确保日期之后有正确的具体的格式子项
                        if (pos >= n)
                        {
                            std::cerr << "日期之后并无具体的格式子项" << std::endl;
                            return false;
                        }

                        // 接下来就是提取具体的格式子项
                        pos++; // 跳过 {
                        while (pos < n && _pattern[pos] != '}')
                            val += _pattern[pos++];

                        // 判断格式子项的合法性
                        if (val.empty() || _pattern[pos] != '}')
                        {
                            std::cerr << "提取到了错误的日期格式子项" << std::endl;
                            return false;
                        }
                        pos++;  // 跳过 }
                    }
                }

                // 将提取到的 其他字符串 或 格式化字符串 加载
                // 非法情况：key 和 val 都为空
                if (key.empty() && val.empty())
                {
                    std::cerr << "错误的信息，无法被加载" << std::endl;
                    return false;
                }

                fmt_order.push_back(std::make_pair(key, val));
                key.clear();
                val.clear();
            }

            // 根据解析出来的格式化信息，生成对应的格式化对象
            for (auto &fmt : fmt_order)
                _items.push_back(CreateItem(fmt.first, fmt.second));

            return true;
        }

        // 不同的格式化字符创建不同的格式化子项对象
        FormatItem::ptr CreateItem(const std::string &key, const std::string &val)
        {
            // 时间、日志器、线程ID、源文件名称、源文件行号、日志等级、制表符、日志主体消息、换行
            // %d    %c     %t      %f         %l          %p       %T     %m           %n
            // 根据不同的 key 返回不同的格式化子项对象
            if (key == "d")
                return std::make_shared<TimeFormatItem>(val);
            if (key == "c")
                return std::make_shared<LoggerFormatItem>();
            if (key == "t")
                return std::make_shared<ThreadIDFormatItem>();
            if (key == "f")
                return std::make_shared<FileFormatItem>();
            if (key == "l")
                return std::make_shared<LineFormatItem>();
            if (key == "p")
                return std::make_shared<LevelFormatItem>();
            if (key == "T")
                return std::make_shared<TabFormatItem>();
            if (key == "m")
                return std::make_shared<MsgFormatItem>();
            if (key == "n")
                return std::make_shared<NewLineFormatItem>();

            // 其他情况
            return std::make_shared<OtherFormatItem>(val);
        }

    private:
        std::string _pattern; // 字符串格式化规则
        std::vector<FormatItem::ptr> _items;
    };
}

#endif